package cn.tworice.system.service.role;

import cn.tworice.common.util.ArrayUtils;
import cn.tworice.mybatis.vo.PageParam;
import cn.tworice.system.service.resources.CacheConst;
import cn.tworice.mybatis.query.LambdaQueryWrapperX;
import cn.tworice.mybatis.query.MPJLambdaWrapperX;
import cn.tworice.system.dao.role.RoleResourcesDao;
import cn.tworice.system.dao.role.UserRoleDao;
import cn.tworice.system.dao.role.po.UserRoleDO;
import cn.tworice.system.dao.user.po.UserDO;
import cn.tworice.system.dao.role.po.RoleDO;
import cn.tworice.system.dao.role.po.RoleResourcesDO;
import cn.tworice.system.dao.role.RoleDao;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;


/**
 * 角色管理服务
 */
@Service
@Transactional
public class RoleServiceImpl extends ServiceImpl<RoleDao, RoleDO> implements RoleService {

    @Resource
    private RoleDao roleDao;

    @Resource
    private UserRoleDao userRoleDao;

    @Resource
    private RoleResourcesDao roleResourcesDao;

    /**
     * 获取所有角色列表
     *
     * @author 二饭
     */
    @Override
    public Map<String, Object> getRoleList(PageParam pageParam, RoleDO entity) {
        return roleDao.selectPage(pageParam, new LambdaQueryWrapperX<RoleDO>()
                .eqIfPresent(RoleDO::getId, entity.getId())
                .likeIfPresent(RoleDO::getRoleName, entity.getRoleName())
                .orderByAsc(RoleDO::getId)
        );
    }

    /**
     * 通过用户编号获取角色列表
     *
     * @param userId 用户
     * @return java.util.List<cn.tworice.system.dao.role.po.RoleDO>
     **/
    @Override
    public List<RoleDO> getRoleListByUid(String userId) {
        return roleDao.selectJoinList(RoleDO.class, new MPJLambdaWrapperX<RoleDO>()
                .selectAll(RoleDO.class)
                .leftJoin(UserRoleDO.class, UserRoleDO::getRoleId, RoleDO::getId)
                .eq(UserRoleDO::getUserId, userId)
        );
    }

    /**
     * 添加人员到角色
     *
     * @author 二饭
     */
    @Override
    public boolean addUserToRole(String[] userIds, Integer roleId) {
        CacheConst.clearRoles(userIds);
        CacheConst.clearResources(userIds);

        // 判断该角色中有没有该人员
        if (userRoleDao.selectCount(new LambdaQueryWrapperX<UserRoleDO>()
                .eq(UserRoleDO::getRoleId, roleId)
                .in(UserRoleDO::getUserId, Arrays.asList(userIds))
        ) == 0
        ) {
            return userRoleDao.insertBatch(this.buildUserRoleList(userIds, roleId), userIds.length);
        }
        return false;
    }

    /**
     * 从角色中移除人员
     *
     * @author 二饭
     */
    @Override
    public void delAdminToRole(String[] adminIds, Integer roleId) {
        CacheConst.clearRoles(adminIds);
        CacheConst.clearResources(adminIds);

        userRoleDao.delete(new LambdaQueryWrapperX<UserRoleDO>()
                .eq(UserRoleDO::getRoleId, roleId)
                .in(UserRoleDO::getUserId, Arrays.asList(adminIds)));
    }

    /**
     * TODO 获取不是该角色的人员，待优化
     * 手写sql -> mybatisplus
     *
     * @author 二饭
     */
    @Override
    public List<UserDO> getNoRoleAdmin(int roleId) {
        return roleDao.queryNoRoleAdmin(roleId);
    }

    /**
     * 通过角色ID查询资源
     *
     * @author 二饭
     */
    @Override
    public List<RoleResourcesDO> getResourcesByRoleId(Integer roleId) {
        return roleResourcesDao.selectList(new LambdaQueryWrapperX<RoleResourcesDO>()
                .eq(RoleResourcesDO::getRoleId, roleId));
    }

    /**
     * 更新角色权限资源
     *
     * @author 二饭
     */
    @Override
    public Boolean updateRoleResources(Integer[] haveResources, Integer[] updateResources, Integer roleId) {
        CacheConst.RESOURCES.clear();

        List<Integer> addResources = new ArrayList<>();
        List<Integer> rmResources = new ArrayList<>();
        // 计算增加的资源权限
        for (Integer i : updateResources) {
            // 该ID原来未被选中
            if (!Arrays.asList(haveResources).contains(i)) {
                addResources.add(i);
            }
        }
        // 计算删除的资源权限
        for (Integer i : haveResources) {
            // 该ID现在被取消
            if (!Arrays.asList(updateResources).contains(i)) {
                rmResources.add(i);
            }
        }

        if (!addResources.isEmpty()) {
            roleResourcesDao.insertBatch(buildRoleRes(addResources, roleId));
        }
        if (!rmResources.isEmpty()) {
            roleResourcesDao.delete(new LambdaQueryWrapperX<RoleResourcesDO>()
                    .in(RoleResourcesDO::getResourceId, rmResources)
                    .eq(RoleResourcesDO::getRoleId, roleId));
        }

        return true;
    }


    /**
     * 删除角色
     * 需要同时删除 角色表 和 角色资源表 所有相关内容
     *
     * @author 二饭
     */
    @Override
    public boolean delRole(List<Integer> roleIdList) {
        CacheConst.ROLES.clear();
        CacheConst.RESOURCES.clear();

        // 同时删除角色相关的 人员角色、角色权限相关数据信息
        if (roleDao.deleteBatchIds(roleIdList) >= 1) {
            roleResourcesDao.delete(new LambdaQueryWrapperX<RoleResourcesDO>()
                    .in(RoleResourcesDO::getRoleId, roleIdList));
            userRoleDao.delete(new LambdaQueryWrapperX<UserRoleDO>()
                    .in(UserRoleDO::getRoleId, roleIdList));
        }
        return true;
    }

    /**
     * 更新或插入角色
     *
     * @author 二饭
     */
    @Override
    public int editRole(Integer roleId, String roleName) {
        int i;
        if (roleId < 1) {
            // 插入
            i = roleDao.insert(new RoleDO().setRoleName(roleName));
        } else {
            // 更新
            i = roleDao.updateById(new RoleDO().setId(roleId).setRoleName(roleName));
        }
        return i;
    }

    /**
     * 更新角色的资源权限
     *
     * @author 二饭
     */
    @Override
    public void setRoleResources(Integer roleId, Integer[] resources) {
        CacheConst.RESOURCES.clear();

        // 删除原有资源权限
        roleResourcesDao.delete(new LambdaQueryWrapperX<RoleResourcesDO>()
                .eq(RoleResourcesDO::getRoleId, roleId));
        // 设置新的资源权限（资源的叶子节点）
        if (ArrayUtils.isNullOrEmpty(resources)) {
            resources = new Integer[0];
        }
        roleResourcesDao.insertBatch(buildRoleRes(Arrays.asList(resources), roleId));
    }

    /**
     * 通过人员id删除 人员角色关系信息
     *
     * @param ids 人员ID
     **/
    @Override
    public void delAdminRoleByAdmin(String[] ids) {
        CacheConst.clearRoles(ids);
        CacheConst.clearResources(ids);
        userRoleDao.delete(new LambdaQueryWrapperX<UserRoleDO>()
                .in(UserRoleDO::getUserId, Arrays.asList(ids)));
    }

    @Override
    public List<Integer> getRoleIdsByUid(String userId) {
        return userRoleDao.selectJoinList(Integer.class, new MPJLambdaWrapperX<UserRoleDO>()
                .select(UserRoleDO::getRoleId)
                .leftJoin(UserDO.class, UserDO::getId, UserRoleDO::getUserId)
                .eq(UserRoleDO::getUserId, userId));
    }

    @Override
    public void removeResourcesInfo(Integer[] ids) {
        roleResourcesDao.delete(new LambdaQueryWrapperX<RoleResourcesDO>()
                .in(RoleResourcesDO::getRoleId, Arrays.asList(ids)));
    }

    /**
     * 根据用户编号数组和角色ID生成角色用户关系列表
     *
     * @param users 用户列表
     * @param role  角色
     * @return java.util.List<cn.tworice.system.dao.role.po.UserRoleDO>
     **/
    private List<UserRoleDO> buildUserRoleList(String[] users, int role) {
        List<UserRoleDO> userRoles = new ArrayList<>();
        Arrays.stream(users).forEach(item -> {
            userRoles.add(new UserRoleDO().setUserId(item).setRoleId(role));
        });
        return userRoles;
    }

    private List<RoleResourcesDO> buildRoleRes(List<Integer> resourceIds, Integer roleId) {
        List<RoleResourcesDO> roleResourcesList = new ArrayList<>();
        resourceIds.forEach(item -> {
            roleResourcesList.add(new RoleResourcesDO().setResourceId(item).setRoleId(roleId));
        });
        return roleResourcesList;
    }
}